#include "PreCompile.hpp"
#include "InStreamTests.hpp"

namespace BuildNG
{
  static const int test_stream_size = 8129;

  MemoryInStream* InStreamTests::create_test_stream()
  {
    guint8 *data = new guint8[test_stream_size];
    for(int i = 0;i < test_stream_size;i++)
      data[i] = (guint8)(i & 0xff);
    MemoryInStream *ret = new MemoryInStream(data,test_stream_size,MemoryInStream::Flag_DeleteData,512);

    return ret;
  }

  void InStreamTests::test_read_buffer()
  {
    MemoryInStream *stream = create_test_stream();

    const int read_chunk_sizes[] =
    {
      256,
      255,
      257,
      64,
      64,
      64,
      64,
      512,
      1,
      512,
      255,
      1,
      254,
      1,
    };

    int total = 0;
    for(int i = 0;i < (int)(sizeof(read_chunk_sizes) / sizeof(int));i++)
      total += read_chunk_sizes[i];
    assert(total < test_stream_size);

    guint8 cur = 0;
    for(int i = 0;i < (int)(sizeof(read_chunk_sizes) / sizeof(int));i++)
    {
      guint8 dest[512];
      stream->read_buffer(dest,read_chunk_sizes[i]);

      for(int j = 0;j < read_chunk_sizes[i];j++)
      {
        assert(dest[j] == cur);
        cur++;
      }
    }

    delete stream;
  }

  void InStreamTests::test_read_uint8()
  {
    MemoryInStream *stream = create_test_stream();

    guint8 cur = 0;
    for(int i = 0;i < test_stream_size;i++)
    {
      assert(stream->read_uint8() == cur);
      cur++;
    }

    delete stream;
  }

  void InStreamTests::test_read_uint16()
  {
    MemoryInStream *stream = create_test_stream();

    guint8 cur = 0;
    for(int j = 0;j < 2;j++)
    {
      for(int i = 0;i < 512;i++)
      {
        guint16 val = stream->read_uint16();

        union
        {
          guint8 comparand_arr[2];
          guint16 comparand;
        };
        comparand_arr[0] = cur++;
        comparand_arr[1] = cur++;
        assert(val == comparand);
      }

      // skip one, so the source will not be aligned anymore, both the memory alignment,
      // and the alignment to the buffer itself (ie. one value will cross the buffer boundary)
      cur++;
      stream->read_uint8();
    }

    delete stream;
  }

  void InStreamTests::test_read_uint32()
  {
    MemoryInStream *stream = create_test_stream();

    guint8 cur = 0;
    for(int j = 0;j < 4;j++)
    {
      for(int i = 0;i < 256;i++)
      {
        guint32 val = stream->read_uint32();

        union
        {
          guint8 comparand_arr[4];
          guint32 comparand;
        };
        comparand_arr[0] = cur++;
        comparand_arr[1] = cur++;
        comparand_arr[2] = cur++;
        comparand_arr[3] = cur++;
        assert(val == comparand);
      }

      // skip one, so the source will not be aligned anymore, both the memory alignment,
      // and the alignment to the buffer itself (ie. one value will cross the buffer boundary)
      cur++;
      stream->read_uint8();
    }

    delete stream;
  }

  void InStreamTests::test_read_bool()
  {
    MemoryInStream *stream = create_test_stream();
    for(int i = 0;i < 256;i++)
    {
      assert(stream->read_bool() == (i != 0));
    }
    delete stream;
  }

  void InStreamTests::test_read_line_simple()
  {
    char data[] = "this is a line of text,\n"
      "and so is this one.\n"
      "empty lines shouldn't be a problem either:\n"
      "\n"
      "and the last one.";
    MemoryInStream stream(data,(int)strlen(data),0);

    Glib::ustring line;

    bool ret = stream.read_line(line);
    assert(ret == true);
    assert(line == "this is a line of text,");

    ret = stream.read_line(line);
    assert(ret == true);
    assert(line == "and so is this one.");

    ret = stream.read_line(line);
    assert(ret == true);
    assert(line == "empty lines shouldn't be a problem either:");

    ret = stream.read_line(line);
    assert(ret == true);
    assert(line == "");

    ret = stream.read_line(line);
    assert(ret == true);
    assert(line == "and the last one.");

    ret = stream.read_line(line);
    assert(ret == false);
  }

  void InStreamTests::test_read_line_special_cases()
  {
    char data[] =
      "0123456789\r\n"
      "012345678\r\n"
      "012345678\r\n";
    MemoryInStream stream(data,(int)strlen(data),0,10);

    Glib::ustring line;
    bool res = stream.read_line(line);
    assert(res == true);
    assert(line == "0123456789");

    res = stream.read_line(line);
    assert(res == true);
    assert(line == "012345678");

    res = stream.read_line(line);
    assert(res == true);
    assert(line == "012345678");

    res = stream.read_line(line);
    assert(res == true);
    assert(line == "");

    res = stream.read_line(line);
    assert(res == false);
  }

  void InStreamTests::test_mixed_newline_chars()
  {
    char data[] =
      "line 1\n"
      "line 2\n\r"
      "line 3\r\n"
      "line 4\r"
      "line 5\n\n"
      "line 7\r\r"
      "line 9\r\n\n\r\n"
      "line 12";

    MemoryInStream stream(data,(int)strlen(data),0,10);

    Glib::ustring line;
    bool res = stream.read_line(line);
    assert(res == true);
    assert(line == "line 1");

    res = stream.read_line(line);
    assert(res == true);
    assert(line == "line 2");

    res = stream.read_line(line);
    assert(res == true);
    assert(line == "line 3");

    res = stream.read_line(line);
    assert(res == true);
    assert(line == "line 4");

    res = stream.read_line(line);
    assert(res == true);
    assert(line == "line 5");

    res = stream.read_line(line);
    assert(res == true);
    assert(line == "");

    res = stream.read_line(line);
    assert(res == true);
    assert(line == "line 7");

    res = stream.read_line(line);
    assert(res == true);
    assert(line == "");

    res = stream.read_line(line);
    assert(res == true);
    assert(line == "line 9");

    res = stream.read_line(line);
    assert(res == true);
    assert(line == "");

    res = stream.read_line(line);
    assert(res == true);
    assert(line == "");

    res = stream.read_line(line);
    assert(res == true);
    assert(line == "line 12");

    res = stream.read_line(line);
    assert(res == false);
  }

  void InStreamTests::test_invalid_utf8_line()
  {
    char data[] = "xx <-- those xx's will be replaced with invalid utf8 on the following lines.\n";
    data[0] = (char)0xf0;
    data[1] = (char)0x25;

    MemoryInStream stream(data,(int)strlen(data),0,10);

    try
    {
      Glib::ustring line;
      stream.read_line(line);

      assert(!"An InStreamInvalidUTF8Exception should have been thrown.");
    }
    catch(InStreamInvalidUTF8Exception&)
    {
      // this is the expected behaviour
    }
  }

  void InStreamTests::test_std_instream()
  {
    std::istringstream str(
      "ek hlewagastiR holtijaR\n"
      "horna tawido\n");
    StdInStream stream(&str,false);
    
    bool line_read;
    Glib::ustring line;
    
    line_read = stream.read_line(line);
    assert(line_read == true);
    assert(line == "ek hlewagastiR holtijaR");

    line_read = stream.read_line(line);
    assert(line_read == true);
    assert(line == "horna tawido");

    line_read = stream.read_line(line);
    assert(line_read == true);
    assert(line == "");

    line_read = stream.read_line(line);
    assert(line_read == false);
  }
}

